package com.agileai.hotweb.domain;

import java.io.StringWriter;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import com.agileai.domain.DataParam;
import com.agileai.domain.DataRow;
import com.agileai.hotweb.common.BeanFactory;
import com.agileai.hotweb.common.DaoHelper;
import com.agileai.hotweb.common.StringTemplateLoader;
import com.agileai.util.DBUtil;
import com.agileai.util.MapUtil;
import com.agileai.util.StringUtil;

import freemarker.template.Configuration;
import freemarker.template.ObjectWrapper;
import freemarker.template.Template;

public class FormSelectFactory {
	public static final String CODELIST_STATEMENT_ID = "util.getCodeList";
	public static final String CODETYPE_STATEMENT_ID = "codetype.getRecord";
	private static final String BEAN_ID = "formSelectFactory";
	private static final String TYPE_ID = "TYPE_ID";
	private static Map<String,List<DataRow>> codeListCache = new HashMap<String,List<DataRow>>();
	private static Map<String,DataRow> codeTypeCache = new HashMap<String,DataRow>();
	
	private DaoHelper daoHelper = null;
	
	public FormSelectFactory(){
	}
	
	public DaoHelper getDaoHelper() {
		return daoHelper;
	}
	
	public void setDaoHelper(DaoHelper daoHelper) {
		this.daoHelper = daoHelper;
	}
	
	public static void cleanCache(String codeType){
		if (codeListCache.containsKey(codeType)){
			codeListCache.remove(codeType);
		}
		if (codeTypeCache.containsKey(codeType)){
			codeTypeCache.remove(codeType);
		}
	}
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private void buildCodeTypeRow8FormSelect(DataRow codeTypeDefineRow,String codeType,
			DataParam queryParam,FormSelect formSelect,String idField,String valueField){
		Connection connection = null;
		try {
			if (codeTypeDefineRow.isEmpty()){
				connection = daoHelper.getDataSource().getConnection();
				connection.setAutoCommit(false);
				StringBuffer codeTypeSql = new StringBuffer();
				codeTypeSql.append("SELECT * FROM sys_codetype WHERE TYPE_ID = '").append(codeType).append("'");
				Statement  statement = null;
				ResultSet rs = null;
				
				statement = connection.createStatement(); 
				rs = statement.executeQuery(codeTypeSql.toString());
				if (rs != null){
					ResultSetMetaData metaData = rs.getMetaData();
					int count = metaData.getColumnCount();
					if (rs.next()) {
						for (int i=1;i <= count;i++){
							String colName = metaData.getColumnLabel(i);
							Object colValue = rs.getObject(colName);
							if (colValue != null){
								codeTypeDefineRow.put(colName,colValue);
							}
						}
					}
				}
				DBUtil.close(statement, rs);
			}
			String extendSQL = (String)codeTypeDefineRow.get("EXTEND_SQL");
			if (!StringUtil.isNullOrEmpty(extendSQL) && "Y".equals(extendSQL)){
				String sqlBody = codeTypeDefineRow.getString("SQL_BODY");
				String sqlCond = codeTypeDefineRow.getString("SQL_COND");
				if (!StringUtil.isNullOrEmpty(sqlBody)){
					StringBuffer sql = new StringBuffer(sqlBody);
					if (!StringUtil.isNullOrEmpty(sqlCond)){
						if (queryParam != null && !queryParam.isEmpty()){
							sqlCond = this.mergeQueryParamSQL(sqlCond, queryParam);
						}
						sql.append("\r\n").append(sqlCond);
						
					}
					if (connection == null){
						connection = daoHelper.getDataSource().getConnection();
						connection.setAutoCommit(false);
					}
					List<HashMap> records = DBUtil.getRecords(connection, sql.toString());
					List<DataRow> list = new ArrayList<DataRow>();
					if (records != null){
						String idName = FormSelect.DEF_KEY_COLUMN_NAME;
						if (!StringUtil.isNullOrEmpty(idField)){
							idName = idField;
						}
						String valueName = FormSelect.DEF_VALUE_COLUMN_NAME;
						if (valueField != null){
							valueName = valueField;
						}
						for (int i=0;i < records.size();i++){
							HashMap tempRow = records.get(i);
							DataRow row = new DataRow();
							row.put(FormSelect.DEF_KEY_COLUMN_NAME,tempRow.get(idName));
							row.put(FormSelect.DEF_VALUE_COLUMN_NAME,tempRow.get(valueName));
							list.add(row);
						}
						formSelect.putValues(list);
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally{
			DBUtil.close(connection);
		}
	}
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public String mergeQueryParamSQL(String template,DataParam param){
		String encoding = "utf-8";
		String result = null;
		try {
			StringWriter writer = new StringWriter();
        	Configuration cfg = new Configuration();
        	cfg.setTemplateLoader(new StringTemplateLoader(template));  
        	cfg.setEncoding(Locale.getDefault(), encoding);
            cfg.setObjectWrapper(ObjectWrapper.BEANS_WRAPPER);
        	Template temp = cfg.getTemplate("");
        	temp.setEncoding(encoding);
            Map root = new HashMap();
            root.putAll(param);
            temp.process(root, writer);
            writer.flush();
            result = writer.toString();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}
	
	private FormSelect build(String codeType){
		FormSelect select = new FormSelect();
		DataRow codeTypeDefineRow = null;
		if (codeTypeCache.containsKey(codeType)){
			codeTypeDefineRow = codeTypeCache.get(codeType);
		}else{
			codeTypeDefineRow = new DataRow();
			this.buildCodeTypeRow8FormSelect(codeTypeDefineRow,codeType, null, select,null,null);
		}
		String extendSQL = (String)codeTypeDefineRow.get("EXTEND_SQL");
		if (StringUtil.isNullOrEmpty(extendSQL) || "N".equals(extendSQL)){
			List<DataRow> tempList = null;
			if (codeListCache.containsKey(codeType)){
				tempList = codeListCache.get(codeType);
			}else{
				DataParam param = new DataParam();
				param.put(TYPE_ID,codeType);
				DataRow codeTypeRow = this.daoHelper.getRecord(CODETYPE_STATEMENT_ID, param);
				tempList = this.daoHelper.queryRecords(CODELIST_STATEMENT_ID, param);
				if (!MapUtil.isNullOrEmpty(codeTypeRow) && "Y".equals(codeTypeRow.getString("IS_CACHED"))){
					codeListCache.put(codeType, tempList);
				}
			}
			List<DataRow> list = new ArrayList<DataRow>();
			for (int i=0;i < tempList.size();i++){
				DataRow row = tempList.get(i);
				row.put(FormSelect.DEF_KEY_COLUMN_NAME,row.get(FormSelect.DEF_KEY_COLUMN_NAME));
				row.put(FormSelect.DEF_VALUE_COLUMN_NAME,row.getString(FormSelect.DEF_VALUE_COLUMN_NAME));
				list.add(row);
			}
			select.putValues(list);
		}
		return select;
	}
	
	private FormSelect build(String codeType,DataParam queryParam,String idField,String valueField){
		FormSelect select = new FormSelect();
		DataRow codeTypeDefineRow = null;
		if (codeTypeCache.containsKey(codeType)){
			codeTypeDefineRow = codeTypeCache.get(codeType);
		}else{
			codeTypeDefineRow = new DataRow();
			this.buildCodeTypeRow8FormSelect(codeTypeDefineRow,codeType, queryParam, select,idField,valueField);
		}
		String extendSQL = (String)codeTypeDefineRow.get("EXTEND_SQL");
		if (StringUtil.isNullOrEmpty(extendSQL) || "N".equals(extendSQL)){
			List<DataRow> tempList = null;
			if (codeListCache.containsKey(codeType)){
				tempList = codeListCache.get(codeType);
			}else{
				DataParam param = new DataParam();
				param.put(TYPE_ID,codeType);
				DataRow codeTypeRow = this.daoHelper.getRecord(CODETYPE_STATEMENT_ID, param);
				tempList = this.daoHelper.queryRecords(CODELIST_STATEMENT_ID, param);
				if (!MapUtil.isNullOrEmpty(codeTypeRow) && "Y".equals(codeTypeRow.getString("IS_CACHED"))){
					codeListCache.put(codeType, tempList);
				}
			}
			List<DataRow> list = new ArrayList<DataRow>();
			for (int i=0;i < tempList.size();i++){
				DataRow row = tempList.get(i);
				row.put(FormSelect.DEF_KEY_COLUMN_NAME,row.get(FormSelect.DEF_KEY_COLUMN_NAME));
				row.put(FormSelect.DEF_VALUE_COLUMN_NAME,row.getString(FormSelect.DEF_VALUE_COLUMN_NAME));
				list.add(row);
			}
			select.putValues(list);			
		}
		return select;
	}	
	
	private static FormSelectFactory formSelectFactory(){
		FormSelectFactory service = (FormSelectFactory)BeanFactory.instance().getBean(BEAN_ID);
		return service;
	}
	
	public static FormSelect create(String codeType){
		return formSelectFactory().build(codeType);
	}
	
	public static FormSelect create(String statementIdOrCodeType,DataParam param){
		FormSelect select = new FormSelect();
		if (statementIdOrCodeType.startsWith("util.")){
			String statementId = statementIdOrCodeType;
			List<DataRow> tempList = formSelectFactory().daoHelper.queryRecords(statementId, param);
			List<DataRow> list = new ArrayList<DataRow>();
			for (int i=0;i < tempList.size();i++){
				DataRow row = tempList.get(i);
				row.put(FormSelect.DEF_KEY_COLUMN_NAME,row.get(FormSelect.DEF_KEY_COLUMN_NAME));
				row.put(FormSelect.DEF_VALUE_COLUMN_NAME,row.getString(FormSelect.DEF_VALUE_COLUMN_NAME));
				list.add(row);
			}
			select.putValues(list);
		}else{
			String codeType = statementIdOrCodeType;
			select = formSelectFactory().build(codeType,param,null,null);
		}
		return select;
	}
	
	public static FormSelect create(String statementIdOrCodeType,DataParam param,String IdName,String valueName){
		FormSelect select = new FormSelect();
		if (statementIdOrCodeType.startsWith("util.")){
			String statementId = statementIdOrCodeType;
			List<DataRow> tempList = formSelectFactory().daoHelper.queryRecords(statementId, param);
			List<DataRow> list = new ArrayList<DataRow>();
			for (int i=0;i < tempList.size();i++){
				DataRow row = tempList.get(i);
				row.put(FormSelect.DEF_KEY_COLUMN_NAME,row.get(IdName));
				row.put(FormSelect.DEF_VALUE_COLUMN_NAME,row.getString(valueName));
				list.add(row);
			}
			select.putValues(list);
		}else{
			String codeType = statementIdOrCodeType;
			select = formSelectFactory().build(codeType,param,IdName,valueName);
		}
		return select;
	}
	
	
	public static FormSelect createSwitchFormSelect(){
		FormSelect result = new FormSelect();
		result.putValue("Y", "是");
		result.putValue("N", "否");
		return result;
	}
}
